package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.pojos.*;

import com.heima.article.pojos.ApComment;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.article.vos.WmCommentListVo;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.UpdateArticleInfo;
import com.heima.model.behavior.dtos.LoadArticleBehaviorDTO;
import com.heima.model.behavior.pojos.UserBehavior;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;

import com.heima.model.user.pojos.ApUser;
import com.heima.model.wemedia.dtos.StatisticsDto;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.DateUtils;
import com.heima.model.wemedia.dtos.WmCommentLikeDTO;
import com.heima.model.wemedia.dtos.WmCommentListPageReqDTO;
import com.heima.model.wemedia.dtos.WmCommentUpdateStatusDTO;
import com.heima.model.wemedia.vos.WmCommentLikeVo;
import com.heima.utils.thread.ApUserThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 陈辉
 * @data 2023 15:41
 */
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    @Autowired
    private ApArticleMapper ApArticleMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private MongoTemplate mongoTemplate;



    /**
     * app文章首页加载/加载更多/加载更新
     *
     * @param dto
     * @param type
     * @return
     */
    @Override
    public ResponseResult load(ArticleHomeDto dto, Short type) {
        //1. 参数校验
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2. 数据处理
        //2.1 先处理分页： 最大一页50
        if (dto.getSize() == null) {
            dto.setSize(10);
        } else {
            dto.setSize(Math.min(dto.getSize(), 50));
        }

        //2.2 再处理频道ID
        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //2.3 最后处理时间
        if (dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());
        if (dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());

        ApUser user = ApUserThreadLocalUtil.getUser();

        //  UserBehavior userBehavior  = JSON.parseObject(readBehaviorCount.toString(),UserBehavior.class);

        //3. 业务操作
        // 获取不喜欢的文章id集合

        // 加载文章列表
        List<ApArticle> list = ApArticleMapper.loadArticleList(dto, type);

        // 用于存储满足条件的文章列表
        List<ApArticle> list1 = new ArrayList<>();

        // 遍历文章列表
        for (ApArticle article : list) {
            Long articleId = article.getId();

            // 从缓存中获取用户行为数据
            String data = (String) cacheService.hGet(user.getId().toString(), String.valueOf(articleId));

            // 将缓存中的用户行为数据解析为UserBehavior对象
            UserBehavior userBehavior = JSON.parseObject(data, UserBehavior.class);

            // 如果用户行为为空或者用户行为中的isUnlike属性为false，则将文章添加到list1中
            if (userBehavior == null || userBehavior.getIsUnlike() == false) {
                list1.add(article);
            }
        }

        // 构造包含满足条件的文章列表的ResponseResult对象并返回
        return ResponseResult.okResult(list1);
    }


    /**
     * 保存或修改app文章信息
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveOrUpdateArticle(ArticleDto dto) {
        //参数校验
        if (dto == null || StringUtils.isBlank(dto.getContent())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        ApArticle article = new ApArticle();
        BeanUtils.copyProperties(dto, article);

        //1. 根据文章id来区分：新增/修改
        if (dto.getId() == null) {
            //第一次数据同步：新增处理: ap_article, ap_article_config,ap_article_content
            //对收藏量 评论量 点赞量 浏览量进行初始化值为 0
            article.setCollection(0);
            article.setComment(0);
            article.setLikes(0);
            article.setViews(0);
            save(article);

            //处理文章配置信息
            ApArticleConfig config = new ApArticleConfig(article.getId());
            apArticleConfigMapper.insert(config);

            //处理文章内容
            ApArticleContent content = new ApArticleContent();
            content.setContent(dto.getContent());
            content.setArticleId(article.getId());
            apArticleContentMapper.insert(content);

        } else {
            //不是第一次数据同步：修改处理 : 只能改文章信息和文章内容信息，不能改文章配置信息!
            updateById(article);

            //修改文章内容
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getId()));
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }

        //生成文章详情页，回填: static_url
        articleFreemarkerService.buildArticleToMinIO(article, dto.getContent());

        return ResponseResult.okResult(article.getId());
    }

    /**
     * 加载文章行为
     *
     * @param loadArticleBehaviorDTO
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(LoadArticleBehaviorDTO loadArticleBehaviorDTO) {
        //参数校验
        if (Objects.isNull(loadArticleBehaviorDTO)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //获取文章
        Long articleId = Long.valueOf(loadArticleBehaviorDTO.getArticleId());

        log.info(Thread.currentThread().getName());

        Integer userId = ApUserThreadLocalUtil.getUser().getId();

        //获取redis中的hashmap的值
        String data = (String) cacheService.hGet(userId.toString(), articleId.toString());

        //数据校验
        if (StringUtils.isBlank(data)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //解析参数
        UserBehavior userBehavior = JSON.parseObject(data, UserBehavior.class);

        //设置返回数据
        Map<String, Boolean> resultMap = new HashMap<>();
        resultMap.put("islike", userBehavior.getIsLike());
        resultMap.put("isunlike", userBehavior.getIsUnlike() == true ? true : false);
        resultMap.put("iscollection", userBehavior.getIsCollection() == true ? true : false);
        resultMap.put("isfollow", userBehavior.getIsFollow() == true ? true : false);

        return ResponseResult.okResult(resultMap);
    }


    /**
     * 更新文章评论状态
     *
     * @param wmCommentUpdateStatusDTO
     * @return
     */
    @Override
    public ResponseResult updateCommentStatus(WmCommentUpdateStatusDTO wmCommentUpdateStatusDTO) {
        //参数校验
        if (Objects.isNull(wmCommentUpdateStatusDTO)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //获取文章配置信息
        ApArticleConfig apArticleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery()
                .eq(ApArticleConfig::getArticleId, Long.valueOf(wmCommentUpdateStatusDTO.getArticleId())));

        //判断文章是否存在
        if (Objects.isNull(apArticleConfig)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //判断是关闭还是开启评论
        if (wmCommentUpdateStatusDTO.getOperation() == 0) {

            //关闭   不让任何用户来评论, 同时清除所有评论
            apArticleConfig.setIsComment(false);

            //获取该评论的id
            List<ApComment> commentList = mongoTemplate.find(Query.query(Criteria
                    .where("entryId").is(Long.valueOf(wmCommentUpdateStatusDTO.getArticleId()))), ApComment.class);

            //获取评论id的集合
            List<String> commentIdList = commentList.stream().map(ApComment::getId).collect(Collectors.toList());

            //获取评论回复集合
            List<ApCommentRepayLike> repayLikeList = mongoTemplate.find(new Query(Criteria.where("commentId").in(commentIdList)), ApCommentRepayLike.class);

            //判断评论回复集合是否为空
            if (repayLikeList != null && repayLikeList.size() > 0) {

                List<String> commentRepayIdList = repayLikeList.stream().map(ApCommentRepayLike::getId).collect(Collectors.toList());

                //删除评论回复点赞表数据
                mongoTemplate.remove(new Query(Criteria.where("commentRepayId").in(commentRepayIdList)));
            }

            //删除评论回复表数据
            mongoTemplate.remove(new Query(Criteria.where("commentId").in(commentIdList)), ApCommentLike.class);

            //删除评论点赞表数据
            mongoTemplate.remove(new Query(Criteria.where("commentId").in(commentIdList)), ApCommentReply.class);

            //删除该文章的所有评论 先删除评论回复和评论点赞
            mongoTemplate.remove(new Query(Criteria.where("entryId").is(Long.valueOf(wmCommentUpdateStatusDTO.getArticleId()))), ApComment.class);

            //将apArticle中的文章评论设置为0
            ApArticle apArticle = getById(wmCommentUpdateStatusDTO.getArticleId());
            apArticle.setComment(0);
            updateById(apArticle);

            //将文章配置信息中的是否可评论设置为不可评论
            apArticleConfig.setIsComment(false);

        } else {

            //开启   可以让用户来进行评论文章
            apArticleConfig.setIsComment(true);

            //将文章配置信息中的是否可评论设置为可评论
            apArticleConfig.setIsComment(true);
        }

        //将文章配置更新到数据库中
        apArticleConfigMapper.updateById(apArticleConfig);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 查看评论列表
     *
     * @param wmCommentListPageReqDTO
     * @return
     */
    @Override
    public ResponseResult commentList(WmCommentListPageReqDTO wmCommentListPageReqDTO) { //参数校验
        if (Objects.isNull(wmCommentListPageReqDTO)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        wmCommentListPageReqDTO.checkParam();

        if (StringUtils.isBlank(wmCommentListPageReqDTO.getArticleId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //通过文章id获取所有评论信息
        Long articleId = Long.valueOf(wmCommentListPageReqDTO.getArticleId());

        //获取所有评论集合
        List<ApComment> apCommentList = mongoTemplate.find(new Query(Criteria.where("entryId").is(articleId)
        ).with(PageRequest.of(wmCommentListPageReqDTO.getPage() - 1, wmCommentListPageReqDTO.getSize()))
                .with(Sort.by(Sort.Direction.DESC, "createdTime")), ApComment.class);

        //准备数据
        List<WmCommentListVo> wmCommentListVos = new ArrayList<>();

        for (ApComment apComment : apCommentList) {
            WmCommentListVo wmCommentListVo = new WmCommentListVo();
            wmCommentListVo.setApComments(apComment);

            //查询评论的所有回复集合
            List<ApCommentReply> apCommentReplyList = mongoTemplate.find(new Query(Criteria.where("commentId").is(apComment.getId()))
                    .with(Sort.by(Sort.Direction.DESC, "createdTime")), ApCommentReply.class);
            wmCommentListVo.setApCommentRepays(apCommentReplyList);
            wmCommentListVos.add(wmCommentListVo);
        }

        return ResponseResult.okResult(wmCommentListVos);
    }

    /**
     * 自媒体端评论点赞
     *
     * @param wmCommentLikeDTO
     * @return
     */
    @Override
    public ResponseResult commentLike(WmCommentLikeDTO wmCommentLikeDTO, Integer authorId) {
        //参数校验
        if (Objects.isNull(wmCommentLikeDTO)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //根据commentId查询对应的评论
        ApComment apComment = mongoTemplate.findOne(new Query(Criteria.where("id").is(wmCommentLikeDTO.getCommentId())), ApComment.class);

        if (Objects.isNull(apComment)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //将评论的状态更新
        if (wmCommentLikeDTO.getOperation() == 0) {
            //先判断是否已经点赞
            ApCommentLike commentLike = mongoTemplate.findOne(new Query(Criteria.where("commentId")
                    .is(wmCommentLikeDTO.getCommentId()).and("authorId").is(authorId)), ApCommentLike.class);
            if (commentLike != null) {
                //说明该用户已经点赞过
                return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH, "用户已经点赞过");
            }
            //点赞
            apComment.setLikes(apComment.getLikes() + 1);

            //更新到点赞表中
            ApCommentLike apCommentLike = new ApCommentLike();
            apCommentLike.setCommentId(wmCommentLikeDTO.getCommentId());
            apCommentLike.setAuthorId(authorId);
            mongoTemplate.save(apCommentLike);
        } else if (wmCommentLikeDTO.getOperation() == 1) {
            //取消点赞
            apComment.setLikes(apComment.getLikes() - 1);
            //删除原有的点赞表数据
            mongoTemplate.remove(new Query(Criteria.where("commentId").is(wmCommentLikeDTO.getCommentId())), ApCommentLike.class);
        }
        //数据更新到评论表中
        mongoTemplate.save(apComment);
        //返回数据到前端
        WmCommentLikeVo wmCommentLikeVo = new WmCommentLikeVo();
        wmCommentLikeVo.setLikes(apComment.getLikes());
        return ResponseResult.okResult(wmCommentLikeVo);
    }


    /**
     * 更新文章信息
     *
     * @param articleInfo
     */
    @Override
    public void updateArticleInfo(UpdateArticleInfo articleInfo) {
        //1.校验参数
        if (articleInfo == null || articleInfo.getArticleId() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.根据文章id查询文章
        ApArticle article = getById(articleInfo.getArticleId());

        //3.校验评论数
        if (articleInfo.getComment() != null && article.getComment() == null) {
            article.setComment(articleInfo.getComment());
        } else if (articleInfo.getComment() != null && article.getComment() != null) {
            //评论数减少到 0 时 ,不可以再减少
            int comment = articleInfo.getComment() + article.getComment();
            comment = articleInfo.getComment() + article.getComment() < 0 ? 0 : comment;
            article.setComment(comment);
        }

        //4.校验收藏数
        if (articleInfo.getCollection() != null && article.getCollection() == null) {
            article.setCollection(articleInfo.getCollection());
        } else if (articleInfo.getCollection() != null && article.getCollection() != null) {
            //评论数减少到 0 时 ,不可以再减少
            int collection = articleInfo.getCollection() + article.getCollection();
            collection = articleInfo.getCollection() + article.getCollection() < 0 ? 0 : collection;
            article.setCollection(collection);
        }

        //5.校验点赞数
        if (articleInfo.getLikes() != null && article.getLikes() == null) {
            article.setLikes(articleInfo.getLikes());
        } else if (articleInfo.getLikes() != null && article.getLikes() != null) {
            article.setLikes(articleInfo.getLikes() + article.getLikes());
        }
        //6.校验阅读数
        if (articleInfo.getCount() != null && article.getViews() == null) {
            article.setViews(articleInfo.getCount());
        } else if (articleInfo.getCount() != null && article.getViews() != null) {
            article.setViews(articleInfo.getCount() + article.getViews());
        }
        //更新文章信息
        updateById(article);
    }

    /**
     * 分页查询新闻数据
     *
     * @param statisticsDto 统计参数对象，包含开始日期、结束日期、当前页码和每页显示条数
     *                      日期格式为"yyyy-MM-dd"，表示查询的新闻数据的时间范围。
     *                      当前页码表示查询的结果的页码数。
     *                      每页显示条数表示每页显示的新闻数据数量。
     *                      统计参数对象中的用户属性为当前自媒体人（作者）信息。
     * @return 分页结果，包含新闻数据列表和总记录数。
     */
    @Override
    public PageResponseResult newPage(StatisticsDto statisticsDto) {
        // 将传入的日期字符串转换为日期对象，设置开始日期和结束日期的时间范围
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate beginDateTime = LocalDate.parse(statisticsDto.getBeginDate(), fmt);
        LocalDate endDateTime = LocalDate.parse(statisticsDto.getEndDate(), fmt);
        LocalDateTime beginDate = LocalDateTime.of(beginDateTime, LocalTime.MIN); 
        LocalDateTime endDate = LocalDateTime.of(endDateTime, LocalTime.MAX);

        // 获取当前自媒体人（作者）
        WmUser user = statisticsDto.getUser();

        // 创建分页查询对象并设置查询条件
        IPage iPage = new Page(statisticsDto.getPage(), statisticsDto.getSize());
        LambdaQueryWrapper<ApArticle> lambdaQueryWrapper = Wrappers.<ApArticle>lambdaQuery()
                .eq(ApArticle::getAuthorId, user.getId())
                .between(ApArticle::getPublishTime, beginDate, endDate)
                .select(ApArticle::getTitle, ApArticle::getLikes, ApArticle::getCollection, ApArticle::getComment, ApArticle::getViews);
        lambdaQueryWrapper.orderByDesc(ApArticle::getPublishTime);
        //遍历  TODO


        // 执行分页查询
        iPage = page(iPage, lambdaQueryWrapper);

            // 封装查询结果到分页响应对象
            PageResponseResult result = new PageResponseResult(statisticsDto.getPage(), statisticsDto.getSize(), (int) iPage.getTotal());
            result.setData(iPage.getRecords());


            return result;
        }




    /**
     * 文章评论数量+1
     *
     * @param entryId 文章ID，表示需要增加评论数的文章的唯一标识符。
     */
    @Override
    public void addOneComment(Long entryId) {
        // 根据文章ID查询文章对象
        ApArticle apArticle = getById(entryId);
        // 如果查询到文章对象
        if (Objects.nonNull(apArticle)) {
            // 将评论数加1
            apArticle.setComment(apArticle.getComment() + 1);
            // 更新文章对象，将增加后的评论数保存到数据库
            updateById(apArticle);
        }
    }

    /**
     * 删除评论数
     *
     * @param num       要删除的评论数，表示需要减少的评论数，可以为负数（减少评论数）或正数（增加评论数）。
     * @param articleId 文章ID，表示需要修改评论数的文章的唯一标识符。
     */
    @Override
    public void deleteCommentNum(Integer num, Long articleId) {
        // 根据文章ID查询文章对象
        ApArticle apArticle = getById(articleId);
        // 如果查询到文章对象
        if (Objects.nonNull(apArticle)) {
            // 将文章对象的评论数增加或减少指定的评论数
            apArticle.setComment(apArticle.getComment() - num);
            // 更新文章对象，将修改后的评论数保存到数据库
            updateById(apArticle);
        }
    }

    /**
     * 通过文章id获取文章信息
     *
     * @param articleId
     * @return
     */
    @Override
    public ApArticle findByArticleId(Long articleId) {
        return getById(articleId);
    }


    /**
     * 根据时间范围统计点赞数量和收藏数量
     *
     * @param
     * @param authorId
     * @param endDateTime
     * @return
     */
    @Override
    public ResponseResult queryLikesAndConllections(Integer authorId, LocalDateTime beginDateTime, LocalDateTime endDateTime) {
        Map map = ApArticleMapper.apAqueryLikesAndConllections(authorId, beginDateTime, endDateTime);
        return ResponseResult.okResult(map);
    }


}
